package main

import (
	"fmt"
	"math"
	"math/rand"
	"time"
)

func main() {
	// 实例化对象，并设速度为0.5
	p := NewPlayer(80)
	//原点
	origin := Vec2{500, 900, 0}
	//轨迹（五角星）
	tracks := []Vec2{{X: 700, Y: 100}, {X: 100, Y: 500}, {X: 900, Y: 500}, {X: 100, Y: 100}}
	p.StartTo(origin)
	// 如果没有到达就一直循环
	i := 1
	for {
		// 每次移动后的位置
		fmt.Println(fmt.Sprintf("%v,%v", p.Pos().X, p.Pos().Y))
		//到达指定地点指定另一地点
		if p.IsArrived() {
			i++
		}
		//回到了顶点
		if i-2 == len(tracks) {
			fmt.Println("回到顶点")
			i = 0
			continue
		}
		if i-1 < len(tracks) {
			p.MoveTo(tracks[i-1])
		} else {
			p.MoveTo(origin)
		}
		//fmt.Println("去往位置",p.targetPos)
		time.Sleep(10000000 * time.Nanosecond)
		// 更新位置
		p.Update()
	}
}

type Vec2 struct {
	X, Y, Z float64
}

// 加
func (v Vec2) Add(other Vec2) Vec2 {

	return Vec2{
		X: v.X + other.X,
		Y: v.Y + other.Y,
		Z: v.Z + other.Z,
	}

}

// 减
func (v Vec2) Sub(other Vec2) Vec2 {

	return Vec2{
		X: v.X - other.X,
		Y: v.Y - other.Y,
		Z: v.Z - other.Z,
	}
}

// 乘
func (v Vec2) Scale(s float64) Vec2 {

	return Vec2{v.X * s, v.Y * s, v.Z * s}
}

// 距离
func (v Vec2) DistanceTo(other Vec2) float64 {
	dx := v.X - other.X
	dy := v.Y - other.Y
	dz := v.Z - other.Z
	return math.Sqrt(dx*dx + dy*dy + dz*dz)
}

// 插值
func (v Vec2) Normalize() Vec2 {
	mag := v.X*v.X + v.Y*v.Y + v.Z*v.Z
	if mag > 0 {
		oneOverMag := 1 / math.Sqrt(mag)
		return Vec2{v.X * oneOverMag, v.Y * oneOverMag, v.Z * oneOverMag}
	}

	return Vec2{0, 0, 0}
}

type Player struct {
	currPos   Vec2    // 当前位置
	targetPos Vec2    // 目标位置
	speed     float64 // 移动速度
}

func (p *Player) StartTo(v Vec2) {

	p.currPos = v
}

// 移动到某个点就是设置目标位置
func (p *Player) MoveTo(v Vec2) {

	p.targetPos = v
}

// 获取当前的位置
func (p *Player) Pos() Vec2 {
	return p.currPos
}

func Rand() int {
	r := rand.New(rand.NewSource(time.Now().UnixNano()))
	return r.Intn(10)
}

// 是否到达
func (p *Player) IsArrived() bool {
	// 通过计算当前位置与目标位置的距离不超过移动的长度，判断已经到达目标点
	return p.currPos.DistanceTo(p.targetPos) < p.speed
}

// 逻辑更新
func (p *Player) Update() {

	if !p.IsArrived() {
		// 计算出当前位置指向目标的朝向
		dir := p.targetPos.Sub(p.currPos).Normalize()

		// 添加速度矢量生成新的位置
		newPos := p.currPos.Add(dir.Scale(p.speed))

		// 移动完成后，更新当前位置
		p.currPos = newPos
	}

}

// 创建
func NewPlayer(speed float64) *Player {

	return &Player{
		speed: speed,
	}
}
